library(factoextra)
Loading required package: ggplot2
Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
library(COTAN)
library(ggrepel)
library(Rtsne)
library(plotly)
Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio

Attaching package: ‘plotly’

The following object is masked from ‘package:ggplot2’:

    last_plot

The following object is masked from ‘package:stats’:

    filter

The following object is masked from ‘package:graphics’:

    layout
library(tidyverse)
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
── Attaching packages ─────────────────────────────────────────────────────────── tidyverse 1.3.0 ──
✓ tibble  3.1.0     ✓ dplyr   1.0.4
✓ tidyr   1.1.2     ✓ stringr 1.4.0
✓ readr   1.4.0     ✓ forcats 0.5.1
✓ purrr   0.3.4     
── Conflicts ────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
x dplyr::filter() masks plotly::filter(), stats::filter()
x dplyr::lag()    masks stats::lag()
library(htmlwidgets)
library(MASS)

Attaching package: ‘MASS’

The following object is masked from ‘package:dplyr’:

    select

The following object is masked from ‘package:plotly’:

    select
library(dendextend)

---------------------
Welcome to dendextend version 1.14.0
Type citation('dendextend') for how to cite the package.

Type browseVignettes(package = 'dendextend') for the package vignette.
The github page is: https://github.com/talgalili/dendextend/

Suggestions and bug-reports can be submitted at: https://github.com/talgalili/dendextend/issues
Or contact: <tal.galili@gmail.com>

    To suppress this message use:  suppressPackageStartupMessages(library(dendextend))
---------------------


Attaching package: ‘dendextend’

The following object is masked from ‘package:stats’:

    cutree
library(grid)
library(ggpubr)

Attaching package: ‘ggpubr’

The following object is masked from ‘package:dendextend’:

    rotate

To demostrate how to do the gene clustering usign COTAN we begin importing the COTAN object that stores all elaborated data and, in this case, regarding a mouse embrionic cortex dataset (developmental stage E17.5).

input_dir = "Data/"
layers = list("L1"=c("Reln","Lhx5"), "L2/3"=c("Satb2","Cux1"), "L4"=c("Rorb","Sox5") , "L5/6"=c("Bcl11b","Fezf2") , "Prog"=c("Vim","Hes1"))
#objE17 = readRDS(file = paste(input_dir,"E17.5_cortex.cotan.RDS", sep = ""))
objE17 = readRDS(file = paste(input_dir,"E17_cortex_cl2.cotan.RDS", sep = ""))
g.space = get.gene.coexpression.space(objE17, 
                                      n.genes.for.marker = 25,
                                      primary.markers = unlist(layers))
[1] "calculating gene coexpression space: output tanh of reduced coex matrix"
     L11      L12    L2/31    L2/32      L41      L42    L5/61    L5/62    Prog1    Prog2 
  "Reln"   "Lhx5"  "Satb2"   "Cux1"   "Rorb"   "Sox5" "Bcl11b"  "Fezf2"    "Vim"   "Hes1" 
[1] "Get p-values on a set of genes on columns genome wide on rows"
[1] "Using function S"
[1] "function to generate S "
[1] "Secondary markers:181"
[1] "function to generate S "
[1] "Columns (V set) number: 181 Rows (U set) number: 1236"
g.space = as.data.frame(as.matrix(g.space))

coex.pca.genes <- prcomp(t(g.space),
                 center = TRUE,
                 scale. = F) 

fviz_eig(coex.pca.genes, addlabels=TRUE,ncp = 10)

#fviz_eig(coex.pca.genes, choice = "eigenvalue", addlabels=TRUE)

Hierarchical clustering

hc.norm = hclust(dist(g.space), method = "ward.D2")

dend <- as.dendrogram(hc.norm)

pca_1 = as.data.frame(coex.pca.genes$rotation[,1:10])
pca_1 = pca_1[order.dendrogram(dend),]

cut = cutree(hc.norm, k = 7, order_clusters_as_data = F)

#- Next lines are only to color and plot the secondary markers

tmp = get.pval(object = objE17,gene.set.col =unlist(layers),gene.set.row = colnames(g.space))
     L11      L12    L2/31    L2/32      L41      L42    L5/61    L5/62    Prog1    Prog2 
  "Reln"   "Lhx5"  "Satb2"   "Cux1"   "Rorb"   "Sox5" "Bcl11b"  "Fezf2"    "Vim"   "Hes1" 
[1] "Get p-values on a set of genes on columns on a set of genes on rows"
[1] "Using function S"
[1] "function to generate S "
for (m in unlist(layers)) {
  tmp = as.data.frame(tmp[order(tmp[,m]),])
  tmp$rank = c(1:nrow(tmp))
  colnames(tmp)[ncol(tmp)] = paste("rank",m,sep = ".")
  }
rank.genes = tmp[,(length(unlist(layers))+1):ncol(tmp)]
for (c in c(1:length(colnames(rank.genes)))) {
  colnames(rank.genes)[c] =strsplit(colnames(rank.genes)[c], split='.',fixed = T)[[1]][2]
}

L1 = rowSums(rank.genes[,layers[[1]]])
L1[layers[[1]]] = 1
L2 = rowSums(rank.genes[,layers[[2]]])
L2[layers[[2]]] = 1
L4 = rowSums(rank.genes[,layers[[3]]])
L4[layers[[3]]] = 1
L5 =rowSums(rank.genes[,layers[[4]]])
L5[layers[[4]]] = 1
P = rowSums(rank.genes[,layers[[5]]])
P[layers[[5]]] = 1
col.secondary = merge(L1,L2,by="row.names",all.x=TRUE)
colnames(col.secondary)[2:3] = c("L1","L2")
rownames(col.secondary) = col.secondary$Row.names
col.secondary = col.secondary[,2:ncol(col.secondary)]
col.secondary = merge(col.secondary,L4,by="row.names",all.x=TRUE)
colnames(col.secondary)[ncol(col.secondary)] = "L4"
rownames(col.secondary) = col.secondary$Row.names
col.secondary = col.secondary[,2:ncol(col.secondary)]
col.secondary = merge(col.secondary,L5,by="row.names",all.x=TRUE)
colnames(col.secondary)[ncol(col.secondary)] = "L5"
rownames(col.secondary) = col.secondary$Row.names
col.secondary = col.secondary[,2:ncol(col.secondary)]
col.secondary = merge(col.secondary,P,by="row.names",all.x=TRUE)
colnames(col.secondary)[ncol(col.secondary)] = "P"
rownames(col.secondary) = col.secondary$Row.names
col.secondary = col.secondary[,2:ncol(col.secondary)]

#  this part is to check that we will color as secondary markers only the genes linked to the
# primary with positive coex
temp.coex = as.matrix(objE17@coex[rownames(objE17@coex) %in% rownames(col.secondary),
                          colnames(objE17@coex) %in% unlist(layers)])
for (n in rownames(col.secondary)) {
  if(any(temp.coex[n,c("Reln","Lhx5")] < 0)){
    col.secondary[n,"L1"] = 100000
  }
  if(any(temp.coex[n,c("Cux1","Satb2")] < 0)){
    col.secondary[n,"L2"] = 100000
  }
  if(any(temp.coex[n,c("Rorb","Sox5")] < 0)){
    col.secondary[n,"L4"] = 100000
  }
  if(any(temp.coex[n,c("Bcl11b","Fezf2")] < 0)){
    col.secondary[n,"L5"] = 100000
  }
  if(any(temp.coex[n,c("Vim","Hes1")] < 0)){
    col.secondary[n,"P"] = 100000
  }
}

mylist.names <- c("L1", "L2", "L4","L5","P")
pos.link  <- vector("list", length(mylist.names))
names(pos.link) <- mylist.names
for (g in rownames(col.secondary)) {
  if(length( which(col.secondary[g,] == min(col.secondary[g,]))) == 1 ){
  pos.link[[which(col.secondary[g,] == min(col.secondary[g,])) ]] = 
    c(pos.link[[which(col.secondary[g,] == min(col.secondary[g,])) ]], g)
  }
}
# ----


pca_1$highlight = with(pca_1, 
          ifelse(rownames(pca_1) %in% pos.link$L5, "genes related to L5/6",
          ifelse(rownames(pca_1) %in% pos.link$L2 , "genes related to L2/3",
          ifelse(rownames(pca_1) %in% pos.link$P , "genes related to Prog" ,
          ifelse(rownames(pca_1) %in% pos.link$L1 , "genes related to L1" ,
          ifelse(rownames(pca_1) %in% pos.link$L4 ,"genes related to L4" ,
      "not marked"))))))

# But sort them based on their order in dend:
#colors_to_use <- pca_1$highlight[order.dendrogram(dend)]

#mycolours <- c("genes related to L5/6" = "#3C5488FF","genes related to L2/3"="#F39B7FFF","genes related to Prog"="#4DBBD5FF","genes related to L1"="#E64B35FF","genes related to L4" = "#91D1C2FF", "not marked"="#B09C85FF")
pca_1$hclust = cut

pca_1$colors = NA
pca_1[pca_1$highlight == "genes related to L5/6", "colors"] = "#3C5488FF"
pca_1[pca_1$highlight == "genes related to L2/3","colors"] = "#F39B7FFF"
pca_1[pca_1$highlight == "genes related to Prog","colors"] = "#4DBBD5FF"
pca_1[pca_1$highlight == "genes related to L1","colors"] = "#E64B35FF"
pca_1[pca_1$highlight == "genes related to L4","colors"] = "#91D1C2FF"
pca_1[pca_1$highlight == "not marked","colors"] = "#B09C85FF"




dend =branches_color(dend,k=7,col=c("#4DBBD5FF","#91D1C2FF","#E64B35FF","gray80","#3C5488FF","#F39B7FFF","gray80" ),groupLabels = T)
dend =color_labels(dend,k=7,labels = rownames(pca_1),col=pca_1$colors)


dend %>%
  dendextend::set("labels", ifelse(labels(dend) %in% rownames(pca_1)[rownames(pca_1) %in% colnames(g.space)] ,labels(dend),"")) %>%
  #  set("branches_k_color", value = c("gray80","#4DBBD5FF","#91D1C2FF" ,"gray80","#F39B7FFF","#E64B35FF","#3C5488FF"), k = 7) %>%
 plot(horiz=F, axes=T,ylim = c(0,80))

cluster = cut
cluster[cluster == 1] = "#4DBBD5FF"
cluster[cluster == 2] = "#91D1C2FF"
cluster[cluster == 3] =  "#E64B35FF"
cluster[cluster == 4] = "#B09C85FF"
cluster[cluster == 5] = "#3C5488FF"
cluster[cluster == 6] = "#F39B7FFF"
cluster[cluster == 7] = "#B09C85FF"


plot.new()
plot(dend,horiz=T, axes=T,xlim = c(100,0),leaflab = "none")
abline(v = 47, lty = 2)
colored_bars(cluster,dend,horiz = T,sort_by_labels_order = F,y_shift = 1,
               rowLabels= "" )
gridGraphics::grid.echo()
tree <- grid.grab()
par_pca = pca_1[colnames(g.space)[colnames(g.space) %in% rownames(pca_1)],]
#plot N 1
p1 <- ggparagraph(text = paste0(rownames(par_pca[par_pca$hclust == par_pca["Fezf2","hclust"],]), collapse = ", "), 
                  face = "italic", 
                  size =10, 
                  color = "#3C5488FF")

#plot N 2
p2 = ggparagraph(text = paste0(rownames(par_pca[par_pca$hclust == unique(par_pca$hclust)[!unique(par_pca$hclust) %in% unique(par_pca[unlist(layers),"hclust"])][1],]), collapse = ", "), 
                 face = "italic", 
                 size =10, 
                 color = "gray")

#plot N 3
p3 = ggparagraph(text = paste0(rownames(par_pca[par_pca$hclust == unique(par_pca$hclust)[!unique(par_pca$hclust) %in% unique(par_pca[unlist(layers),"hclust"])][2],]), collapse = ", "), 
                 face = "italic", 
                 size =10, 
                 color = "gray")
#plot N 4
p4 = ggparagraph(text = paste0(rownames(par_pca[par_pca$hclust == par_pca["Reln","hclust"],]), collapse = ", "), 
                 face = "italic", 
                 size =10, 
                 color = "#E64B35FF")

#plot N 5
p5 = ggparagraph(text = paste0(rownames(par_pca[par_pca$hclust == par_pca["Cux1","hclust"],]), collapse = ", "), 
                 face = "italic", 
                 size =10, 
                 color = par_pca["Cux1","colors"])
#plot N 6
p6 = ggparagraph(text = paste0(rownames(par_pca[par_pca$hclust == par_pca["Rorb","hclust"],]), collapse = ", "), 
                 face = "italic", 
                 size =10, 
                 color = par_pca["Rorb","colors"])
#plot N 7
p7 = ggparagraph(text = paste0(rownames(par_pca[par_pca$hclust == par_pca["Vim","hclust"],]), collapse = ", "), 
                 face = "italic", 
                 size =10, 
                 color = par_pca["Vim","colors"])

w = ggparagraph(text = " ", 
                 face = "italic", 
                 size =10, 
                 color = "white")

pp =ggarrange(p3,p5,p1,p2,p4,p6,p7,w,
          ncol = 1, nrow = 8,
          heights = c(0.1,0.15,0.23, 0.1, 0.2, 0.2, 0.1, 0.35))

    
lay <- rbind(c(1,NA),
             c(1,2.5),
             c(1,2.5),
             c(1,2.5),
             c(1,2.5),
             c(1,2.5),
             c(1,NA))

gridExtra::grid.arrange(tree, pp, layout_matrix = lay)

or just with primary markers

# some more genes as landmarks
controls =list("genes related to L5/6"=c("Foxp2","Tbr1"), "genes related to L2/3"=c("Mef2c"), "genes related to Prog"=c("Nes","Sox2") , "genes related to L1"=c() , "genes related to L4"=c()) 
dend %>%
dendextend::set("labels", ifelse(labels(dend) %in% rownames(pca_1)[rownames(pca_1) %in% c(unlist(layers),unlist(controls))], labels(dend), "")) %>%
  dendextend::set("branches_k_color", value = c("gray80","#4DBBD5FF","#91D1C2FF" ,"gray80","#F39B7FFF","#E64B35FF","#3C5488FF"), k = 7) %>%
plot(horiz=F, axes=T,ylim = c(0,100))

Now we can plot the PCA

set.seed(NULL)
cell = sample(ncol(objE17@raw),1,replace = T)
print(cell)
[1] 676
genes.to.color.red = which(objE17@raw[,cell] > 0)
length(genes.to.color.red)
[1] 1046
pca3 = pca2 + geom_point(data = subset(pca_1, names %in% names(genes.to.color.red)), color="red")

pca3

t-SNE code and plot

# run the t-SNE
cl.genes.tsne = Rtsne(g.space ,initial_dims = 100, dims = 2, perplexity=30,eta = 200, verbose=F, max_iter = 3000,theta=0.4,num_threads = 10,pca_center = T, pca_scale = FALSE, normalize = T )

d_tsne_1 = as.data.frame(cl.genes.tsne$Y)
rownames(d_tsne_1) = rownames(g.space)

d_tsne_1 = d_tsne_1[order.dendrogram(dend),]

# save the cluster numebr inside a dataframe with the t-SNE information
d_tsne_1$hclust = cut

d_tsne_1$names = rownames(d_tsne_1)

# as before to label only some genes
textdf <- d_tsne_1[rownames(d_tsne_1) %in% c(unlist(layers),unlist(controls)),]

for (m in c(1:length(controls))) {
  for (g in controls[[m]]) {
    if(g %in% rownames(textdf)){
      textdf[g,"highlight"] = names(controls[m])
    } 
  }
}


 p1 = ggplot(subset(d_tsne_1,!hclust %in% unique(cut[unlist(layers)])), aes(x=V1, y=V2)) +  geom_point(alpha = 0.3, color = "#B09C85FF", size=1)

p2 = p1 + geom_point(data = subset(d_tsne_1, hclust %in% unique(cut[unlist(layers)]) ), aes(x=V1, y=V2, colour=as.character(hclust)),size=1,alpha = 0.5) +
    scale_color_manual("Status", values = mycolours2)  +
  scale_fill_manual("Status", values = mycolours2)  +
  xlab("") + ylab("")+
  geom_label_repel(data =textdf , aes(x = V1, y = V2, label = names,fill=as.character(hclust)),
                   label.size = NA, 
                   alpha = 0.5, 
                   direction = "both",
                   na.rm=TRUE,
                   seed = 1234) +
  geom_label_repel(data =textdf , aes(x = V1, y = V2, label = names),
                   label.size = NA, 
                   segment.color = 'black',
                   segment.size = 0.5,
                   direction = "both",
                   alpha = 1, 
                   na.rm=TRUE,
                   fill = NA,
                   seed = 1234) +
  ggtitle("t-SNE") +
  theme_light(base_size=10) +
  theme(axis.text.x=element_blank(),plot.title = element_text(size=14, 
                                    face="italic", 
                                    color="#3C5488FF",
                                    hjust=0.01,
                                    lineheight=1.2,margin = margin(t = 5, b = -15)),
        axis.text.y=element_blank(),
        legend.position = "none")  # titl)
p2

Code to create an iteractive plot. This can be modified to be used with all the plots.


p = ggplot(d_tsne_1, aes(x=V1, y=V2, text= paste("gene: ",names))) +  
  geom_point(size=2, aes(colour=as.character(hclust)), alpha=0.8) +
  scale_color_manual("Status", values = mycolours2)  +
  xlab("") + ylab("") +
  ggtitle("t-SNE") +
  theme_light(base_size=10) +
  theme(axis.text.x=element_blank(),
        axis.text.y=element_blank())

ggplotly(p)

Multidimensional scaling (MDS) and plot

sessionInfo()
R version 4.0.4 (2021-02-15)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 18.04.5 LTS

Matrix products: default
BLAS:   /usr/lib/x86_64-linux-gnu/openblas/libblas.so.3
LAPACK: /usr/lib/x86_64-linux-gnu/libopenblasp-r0.2.20.so

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C               LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
 [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8    LC_PAPER=en_US.UTF-8       LC_NAME=C                 
 [9] LC_ADDRESS=C               LC_TELEPHONE=C             LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       

attached base packages:
[1] grid      stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] ggpubr_0.4.0      dendextend_1.14.0 MASS_7.3-53.1     htmlwidgets_1.5.3 forcats_0.5.1     stringr_1.4.0    
 [7] dplyr_1.0.4       purrr_0.3.4       readr_1.4.0       tidyr_1.1.2       tibble_3.0.6      tidyverse_1.3.0  
[13] plotly_4.9.3      Rtsne_0.15        ggrepel_0.9.1     COTAN_0.1.0       factoextra_1.0.7  ggplot2_3.3.3    

loaded via a namespace (and not attached):
 [1] matrixStats_0.58.0   fs_1.5.0             lubridate_1.7.9.2    filelock_1.0.2       RColorBrewer_1.1-2  
 [6] httr_1.4.2           tools_4.0.4          backports_1.2.1      R6_2.5.0             DBI_1.1.1           
[11] lazyeval_0.2.2       BiocGenerics_0.36.0  colorspace_2.0-0     GetoptLong_1.0.5     withr_2.4.1         
[16] tidyselect_1.1.0     gridExtra_2.3        curl_4.3             compiler_4.0.4       cli_2.3.0           
[21] rvest_0.3.6          Cairo_1.5-12.2       basilisk.utils_1.2.2 xml2_1.3.2           labeling_0.4.2      
[26] scales_1.1.1         rappdirs_0.3.3       digest_0.6.27        foreign_0.8-81       rmarkdown_2.7       
[31] rio_0.5.16           basilisk_1.2.1       pkgconfig_2.0.3      htmltools_0.5.1.1    dbplyr_2.1.0        
[36] rlang_0.4.10         GlobalOptions_0.1.2  readxl_1.3.1         rstudioapi_0.13      gridGraphics_0.5-1  
[41] farver_2.0.3         shape_1.4.5          generics_0.1.0       jsonlite_1.7.2       crosstalk_1.1.1     
[46] zip_2.1.1            car_3.0-10           magrittr_2.0.1       Matrix_1.3-2         Rcpp_1.0.6          
[51] munsell_0.5.0        S4Vectors_0.28.1     abind_1.4-5          reticulate_1.18      viridis_0.5.1       
[56] lifecycle_0.2.0      stringi_1.5.3        yaml_2.2.1           carData_3.0-4        parallel_4.0.4      
[61] crayon_1.4.0         lattice_0.20-41      haven_2.3.1          cowplot_1.1.1        circlize_0.4.12     
[66] hms_1.0.0            knitr_1.31           ComplexHeatmap_2.6.2 pillar_1.4.7         rjson_0.2.20        
[71] ggsignif_0.6.1       stats4_4.0.4         reprex_1.0.0         glue_1.4.2           evaluate_0.14       
[76] data.table_1.13.6    modelr_0.1.8         png_0.1-7            vctrs_0.3.6          cellranger_1.1.0    
[81] gtable_0.3.0         clue_0.3-58          assertthat_0.2.1     openxlsx_4.2.3       xfun_0.20           
[86] broom_0.7.5          rstatix_0.7.0        viridisLite_0.3.0    IRanges_2.24.1       cluster_2.1.1       
[91] ellipsis_0.3.1      
LS0tCnRpdGxlOiAiR2VuZV9jbHVzdGVyaW5nIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIGNvbGxhcHNlZDogbm8KICAgIGNzczogaHRtbC1tZC0wMS5jc3MKICAgIGZpZ19jYXB0aW9uOiB5ZXMKICAgIGhpZ2hsaWdodDogaGFkZG9jawogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMKICAgIHRoZW1lOiBzcGFjZWxhYgogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCiAgaHRtbF9ub3RlYm9vazoKICAgIGNvbGxhcHNlZDogbm8KICAgIGNzczogaHRtbC1tZC0wMS5jc3MKICAgIGZpZ19jYXB0aW9uOiB5ZXMKICAgIGhpZ2hsaWdodDogaGFkZG9jawogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMKICAgIHRoZW1lOiBzcGFjZWxhYgogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCi0tLQoKYGBge3IsIGluY2x1ZGUgPSBGQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KAogIGNvbGxhcHNlID0gVFJVRSwKICBjb21tZW50ID0gIiM+IiwKICBmaWcud2lkdGggPSA3LAogIGZpZy5oZWlnaHQgPSA3CikKYGBgCgpgYGB7ciBzZXR1cH0KCmxpYnJhcnkoZmFjdG9leHRyYSkKbGlicmFyeShDT1RBTikKbGlicmFyeShnZ3JlcGVsKQpsaWJyYXJ5KFJ0c25lKQpsaWJyYXJ5KHBsb3RseSkKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoaHRtbHdpZGdldHMpCmxpYnJhcnkoTUFTUykKbGlicmFyeShkZW5kZXh0ZW5kKQpsaWJyYXJ5KGdyaWQpCmxpYnJhcnkoZ2dwdWJyKQpgYGAKClRvIGRlbW9zdHJhdGUgaG93IHRvIGRvIHRoZSBnZW5lIGNsdXN0ZXJpbmcgdXNpZ24gQ09UQU4gd2UgYmVnaW4gaW1wb3J0aW5nIHRoZSBDT1RBTiBvYmplY3QgdGhhdCBzdG9yZXMgYWxsIGVsYWJvcmF0ZWQgZGF0YSBhbmQsIGluIHRoaXMgY2FzZSwgcmVnYXJkaW5nIGEgbW91c2UgZW1icmlvbmljIGNvcnRleCBkYXRhc2V0IChkZXZlbG9wbWVudGFsIHN0YWdlIEUxNy41KS4KCmBgYHtyfQppbnB1dF9kaXIgPSAiRGF0YS8iCmxheWVycyA9IGxpc3QoIkwxIj1jKCJSZWxuIiwiTGh4NSIpLCAiTDIvMyI9YygiU2F0YjIiLCJDdXgxIiksICJMNCI9YygiUm9yYiIsIlNveDUiKSAsICJMNS82Ij1jKCJCY2wxMWIiLCJGZXpmMiIpICwgIlByb2ciPWMoIlZpbSIsIkhlczEiKSkKI29iakUxNyA9IHJlYWRSRFMoZmlsZSA9IHBhc3RlKGlucHV0X2RpciwiRTE3LjVfY29ydGV4LmNvdGFuLlJEUyIsIHNlcCA9ICIiKSkKb2JqRTE3ID0gcmVhZFJEUyhmaWxlID0gcGFzdGUoaW5wdXRfZGlyLCJFMTdfY29ydGV4X2NsMi5jb3Rhbi5SRFMiLCBzZXAgPSAiIikpCmBgYAoKYGBge3J9Cmcuc3BhY2UgPSBnZXQuZ2VuZS5jb2V4cHJlc3Npb24uc3BhY2Uob2JqRTE3LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuLmdlbmVzLmZvci5tYXJrZXIgPSAyNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcmltYXJ5Lm1hcmtlcnMgPSB1bmxpc3QobGF5ZXJzKSkKYGBgCmBgYHtyfQpnLnNwYWNlID0gYXMuZGF0YS5mcmFtZShhcy5tYXRyaXgoZy5zcGFjZSkpCgpjb2V4LnBjYS5nZW5lcyA8LSBwcmNvbXAodChnLnNwYWNlKSwKICAgICAgICAgICAgICAgICBjZW50ZXIgPSBUUlVFLAogICAgICAgICAgICAgICAgIHNjYWxlLiA9IEYpIAoKZnZpel9laWcoY29leC5wY2EuZ2VuZXMsIGFkZGxhYmVscz1UUlVFLG5jcCA9IDEwKQojZnZpel9laWcoY29leC5wY2EuZ2VuZXMsIGNob2ljZSA9ICJlaWdlbnZhbHVlIiwgYWRkbGFiZWxzPVRSVUUpCmBgYApIaWVyYXJjaGljYWwgY2x1c3RlcmluZwoKYGBge3J9CmhjLm5vcm0gPSBoY2x1c3QoZGlzdChnLnNwYWNlKSwgbWV0aG9kID0gIndhcmQuRDIiKQoKZGVuZCA8LSBhcy5kZW5kcm9ncmFtKGhjLm5vcm0pCgpwY2FfMSA9IGFzLmRhdGEuZnJhbWUoY29leC5wY2EuZ2VuZXMkcm90YXRpb25bLDE6MTBdKQpwY2FfMSA9IHBjYV8xW29yZGVyLmRlbmRyb2dyYW0oZGVuZCksXQoKY3V0ID0gY3V0cmVlKGhjLm5vcm0sIGsgPSA3LCBvcmRlcl9jbHVzdGVyc19hc19kYXRhID0gRikKCiMtIE5leHQgbGluZXMgYXJlIG9ubHkgdG8gY29sb3IgYW5kIHBsb3QgdGhlIHNlY29uZGFyeSBtYXJrZXJzCgp0bXAgPSBnZXQucHZhbChvYmplY3QgPSBvYmpFMTcsZ2VuZS5zZXQuY29sID11bmxpc3QobGF5ZXJzKSxnZW5lLnNldC5yb3cgPSBjb2xuYW1lcyhnLnNwYWNlKSkKZm9yIChtIGluIHVubGlzdChsYXllcnMpKSB7CiAgdG1wID0gYXMuZGF0YS5mcmFtZSh0bXBbb3JkZXIodG1wWyxtXSksXSkKICB0bXAkcmFuayA9IGMoMTpucm93KHRtcCkpCiAgY29sbmFtZXModG1wKVtuY29sKHRtcCldID0gcGFzdGUoInJhbmsiLG0sc2VwID0gIi4iKQogIH0KcmFuay5nZW5lcyA9IHRtcFssKGxlbmd0aCh1bmxpc3QobGF5ZXJzKSkrMSk6bmNvbCh0bXApXQpmb3IgKGMgaW4gYygxOmxlbmd0aChjb2xuYW1lcyhyYW5rLmdlbmVzKSkpKSB7CiAgY29sbmFtZXMocmFuay5nZW5lcylbY10gPXN0cnNwbGl0KGNvbG5hbWVzKHJhbmsuZ2VuZXMpW2NdLCBzcGxpdD0nLicsZml4ZWQgPSBUKVtbMV1dWzJdCn0KCkwxID0gcm93U3VtcyhyYW5rLmdlbmVzWyxsYXllcnNbWzFdXV0pCkwxW2xheWVyc1tbMV1dXSA9IDEKTDIgPSByb3dTdW1zKHJhbmsuZ2VuZXNbLGxheWVyc1tbMl1dXSkKTDJbbGF5ZXJzW1syXV1dID0gMQpMNCA9IHJvd1N1bXMocmFuay5nZW5lc1ssbGF5ZXJzW1szXV1dKQpMNFtsYXllcnNbWzNdXV0gPSAxCkw1ID1yb3dTdW1zKHJhbmsuZ2VuZXNbLGxheWVyc1tbNF1dXSkKTDVbbGF5ZXJzW1s0XV1dID0gMQpQID0gcm93U3VtcyhyYW5rLmdlbmVzWyxsYXllcnNbWzVdXV0pClBbbGF5ZXJzW1s1XV1dID0gMQpjb2wuc2Vjb25kYXJ5ID0gbWVyZ2UoTDEsTDIsYnk9InJvdy5uYW1lcyIsYWxsLng9VFJVRSkKY29sbmFtZXMoY29sLnNlY29uZGFyeSlbMjozXSA9IGMoIkwxIiwiTDIiKQpyb3duYW1lcyhjb2wuc2Vjb25kYXJ5KSA9IGNvbC5zZWNvbmRhcnkkUm93Lm5hbWVzCmNvbC5zZWNvbmRhcnkgPSBjb2wuc2Vjb25kYXJ5WywyOm5jb2woY29sLnNlY29uZGFyeSldCmNvbC5zZWNvbmRhcnkgPSBtZXJnZShjb2wuc2Vjb25kYXJ5LEw0LGJ5PSJyb3cubmFtZXMiLGFsbC54PVRSVUUpCmNvbG5hbWVzKGNvbC5zZWNvbmRhcnkpW25jb2woY29sLnNlY29uZGFyeSldID0gIkw0Igpyb3duYW1lcyhjb2wuc2Vjb25kYXJ5KSA9IGNvbC5zZWNvbmRhcnkkUm93Lm5hbWVzCmNvbC5zZWNvbmRhcnkgPSBjb2wuc2Vjb25kYXJ5WywyOm5jb2woY29sLnNlY29uZGFyeSldCmNvbC5zZWNvbmRhcnkgPSBtZXJnZShjb2wuc2Vjb25kYXJ5LEw1LGJ5PSJyb3cubmFtZXMiLGFsbC54PVRSVUUpCmNvbG5hbWVzKGNvbC5zZWNvbmRhcnkpW25jb2woY29sLnNlY29uZGFyeSldID0gIkw1Igpyb3duYW1lcyhjb2wuc2Vjb25kYXJ5KSA9IGNvbC5zZWNvbmRhcnkkUm93Lm5hbWVzCmNvbC5zZWNvbmRhcnkgPSBjb2wuc2Vjb25kYXJ5WywyOm5jb2woY29sLnNlY29uZGFyeSldCmNvbC5zZWNvbmRhcnkgPSBtZXJnZShjb2wuc2Vjb25kYXJ5LFAsYnk9InJvdy5uYW1lcyIsYWxsLng9VFJVRSkKY29sbmFtZXMoY29sLnNlY29uZGFyeSlbbmNvbChjb2wuc2Vjb25kYXJ5KV0gPSAiUCIKcm93bmFtZXMoY29sLnNlY29uZGFyeSkgPSBjb2wuc2Vjb25kYXJ5JFJvdy5uYW1lcwpjb2wuc2Vjb25kYXJ5ID0gY29sLnNlY29uZGFyeVssMjpuY29sKGNvbC5zZWNvbmRhcnkpXQoKIyAgdGhpcyBwYXJ0IGlzIHRvIGNoZWNrIHRoYXQgd2Ugd2lsbCBjb2xvciBhcyBzZWNvbmRhcnkgbWFya2VycyBvbmx5IHRoZSBnZW5lcyBsaW5rZWQgdG8gdGhlCiMgcHJpbWFyeSB3aXRoIHBvc2l0aXZlIGNvZXgKdGVtcC5jb2V4ID0gYXMubWF0cml4KG9iakUxN0Bjb2V4W3Jvd25hbWVzKG9iakUxN0Bjb2V4KSAlaW4lIHJvd25hbWVzKGNvbC5zZWNvbmRhcnkpLAogICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG5hbWVzKG9iakUxN0Bjb2V4KSAlaW4lIHVubGlzdChsYXllcnMpXSkKZm9yIChuIGluIHJvd25hbWVzKGNvbC5zZWNvbmRhcnkpKSB7CiAgaWYoYW55KHRlbXAuY29leFtuLGMoIlJlbG4iLCJMaHg1IildIDwgMCkpewogICAgY29sLnNlY29uZGFyeVtuLCJMMSJdID0gMTAwMDAwCiAgfQogIGlmKGFueSh0ZW1wLmNvZXhbbixjKCJDdXgxIiwiU2F0YjIiKV0gPCAwKSl7CiAgICBjb2wuc2Vjb25kYXJ5W24sIkwyIl0gPSAxMDAwMDAKICB9CiAgaWYoYW55KHRlbXAuY29leFtuLGMoIlJvcmIiLCJTb3g1IildIDwgMCkpewogICAgY29sLnNlY29uZGFyeVtuLCJMNCJdID0gMTAwMDAwCiAgfQogIGlmKGFueSh0ZW1wLmNvZXhbbixjKCJCY2wxMWIiLCJGZXpmMiIpXSA8IDApKXsKICAgIGNvbC5zZWNvbmRhcnlbbiwiTDUiXSA9IDEwMDAwMAogIH0KICBpZihhbnkodGVtcC5jb2V4W24sYygiVmltIiwiSGVzMSIpXSA8IDApKXsKICAgIGNvbC5zZWNvbmRhcnlbbiwiUCJdID0gMTAwMDAwCiAgfQp9CgpteWxpc3QubmFtZXMgPC0gYygiTDEiLCAiTDIiLCAiTDQiLCJMNSIsIlAiKQpwb3MubGluayAgPC0gdmVjdG9yKCJsaXN0IiwgbGVuZ3RoKG15bGlzdC5uYW1lcykpCm5hbWVzKHBvcy5saW5rKSA8LSBteWxpc3QubmFtZXMKZm9yIChnIGluIHJvd25hbWVzKGNvbC5zZWNvbmRhcnkpKSB7CiAgaWYobGVuZ3RoKCB3aGljaChjb2wuc2Vjb25kYXJ5W2csXSA9PSBtaW4oY29sLnNlY29uZGFyeVtnLF0pKSkgPT0gMSApewogIHBvcy5saW5rW1t3aGljaChjb2wuc2Vjb25kYXJ5W2csXSA9PSBtaW4oY29sLnNlY29uZGFyeVtnLF0pKSBdXSA9IAogICAgYyhwb3MubGlua1tbd2hpY2goY29sLnNlY29uZGFyeVtnLF0gPT0gbWluKGNvbC5zZWNvbmRhcnlbZyxdKSkgXV0sIGcpCiAgfQp9CiMgLS0tLQoKCnBjYV8xJGhpZ2hsaWdodCA9IHdpdGgocGNhXzEsIAogICAgICAgICAgaWZlbHNlKHJvd25hbWVzKHBjYV8xKSAlaW4lIHBvcy5saW5rJEw1LCAiZ2VuZXMgcmVsYXRlZCB0byBMNS82IiwKICAgICAgICAgIGlmZWxzZShyb3duYW1lcyhwY2FfMSkgJWluJSBwb3MubGluayRMMiAsICJnZW5lcyByZWxhdGVkIHRvIEwyLzMiLAogICAgICAgICAgaWZlbHNlKHJvd25hbWVzKHBjYV8xKSAlaW4lIHBvcy5saW5rJFAgLCAiZ2VuZXMgcmVsYXRlZCB0byBQcm9nIiAsCiAgICAgICAgICBpZmVsc2Uocm93bmFtZXMocGNhXzEpICVpbiUgcG9zLmxpbmskTDEgLCAiZ2VuZXMgcmVsYXRlZCB0byBMMSIgLAogICAgICAgICAgaWZlbHNlKHJvd25hbWVzKHBjYV8xKSAlaW4lIHBvcy5saW5rJEw0ICwiZ2VuZXMgcmVsYXRlZCB0byBMNCIgLAogICAgICAibm90IG1hcmtlZCIpKSkpKSkKCiMgQnV0IHNvcnQgdGhlbSBiYXNlZCBvbiB0aGVpciBvcmRlciBpbiBkZW5kOgojY29sb3JzX3RvX3VzZSA8LSBwY2FfMSRoaWdobGlnaHRbb3JkZXIuZGVuZHJvZ3JhbShkZW5kKV0KCiNteWNvbG91cnMgPC0gYygiZ2VuZXMgcmVsYXRlZCB0byBMNS82IiA9ICIjM0M1NDg4RkYiLCJnZW5lcyByZWxhdGVkIHRvIEwyLzMiPSIjRjM5QjdGRkYiLCJnZW5lcyByZWxhdGVkIHRvIFByb2ciPSIjNERCQkQ1RkYiLCJnZW5lcyByZWxhdGVkIHRvIEwxIj0iI0U2NEIzNUZGIiwiZ2VuZXMgcmVsYXRlZCB0byBMNCIgPSAiIzkxRDFDMkZGIiwgIm5vdCBtYXJrZWQiPSIjQjA5Qzg1RkYiKQpwY2FfMSRoY2x1c3QgPSBjdXQKCnBjYV8xJGNvbG9ycyA9IE5BCnBjYV8xW3BjYV8xJGhpZ2hsaWdodCA9PSAiZ2VuZXMgcmVsYXRlZCB0byBMNS82IiwgImNvbG9ycyJdID0gIiMzQzU0ODhGRiIKcGNhXzFbcGNhXzEkaGlnaGxpZ2h0ID09ICJnZW5lcyByZWxhdGVkIHRvIEwyLzMiLCJjb2xvcnMiXSA9ICIjRjM5QjdGRkYiCnBjYV8xW3BjYV8xJGhpZ2hsaWdodCA9PSAiZ2VuZXMgcmVsYXRlZCB0byBQcm9nIiwiY29sb3JzIl0gPSAiIzREQkJENUZGIgpwY2FfMVtwY2FfMSRoaWdobGlnaHQgPT0gImdlbmVzIHJlbGF0ZWQgdG8gTDEiLCJjb2xvcnMiXSA9ICIjRTY0QjM1RkYiCnBjYV8xW3BjYV8xJGhpZ2hsaWdodCA9PSAiZ2VuZXMgcmVsYXRlZCB0byBMNCIsImNvbG9ycyJdID0gIiM5MUQxQzJGRiIKcGNhXzFbcGNhXzEkaGlnaGxpZ2h0ID09ICJub3QgbWFya2VkIiwiY29sb3JzIl0gPSAiI0IwOUM4NUZGIgoKCgoKZGVuZCA9YnJhbmNoZXNfY29sb3IoZGVuZCxrPTcsY29sPWMoIiM0REJCRDVGRiIsIiM5MUQxQzJGRiIsIiNFNjRCMzVGRiIsImdyYXk4MCIsIiMzQzU0ODhGRiIsIiNGMzlCN0ZGRiIsImdyYXk4MCIgKSxncm91cExhYmVscyA9IFQpCmRlbmQgPWNvbG9yX2xhYmVscyhkZW5kLGs9NyxsYWJlbHMgPSByb3duYW1lcyhwY2FfMSksY29sPXBjYV8xJGNvbG9ycykKCgpkZW5kICU+JQogIGRlbmRleHRlbmQ6OnNldCgibGFiZWxzIiwgaWZlbHNlKGxhYmVscyhkZW5kKSAlaW4lIHJvd25hbWVzKHBjYV8xKVtyb3duYW1lcyhwY2FfMSkgJWluJSBjb2xuYW1lcyhnLnNwYWNlKV0gLGxhYmVscyhkZW5kKSwiIikpICU+JQogICMgIHNldCgiYnJhbmNoZXNfa19jb2xvciIsIHZhbHVlID0gYygiZ3JheTgwIiwiIzREQkJENUZGIiwiIzkxRDFDMkZGIiAsImdyYXk4MCIsIiNGMzlCN0ZGRiIsIiNFNjRCMzVGRiIsIiMzQzU0ODhGRiIpLCBrID0gNykgJT4lCiBwbG90KGhvcml6PUYsIGF4ZXM9VCx5bGltID0gYygwLDgwKSkKYGBgCmBgYHtyfQpjbHVzdGVyID0gY3V0CmNsdXN0ZXJbY2x1c3RlciA9PSAxXSA9ICIjNERCQkQ1RkYiCmNsdXN0ZXJbY2x1c3RlciA9PSAyXSA9ICIjOTFEMUMyRkYiCmNsdXN0ZXJbY2x1c3RlciA9PSAzXSA9ICAiI0U2NEIzNUZGIgpjbHVzdGVyW2NsdXN0ZXIgPT0gNF0gPSAiI0IwOUM4NUZGIgpjbHVzdGVyW2NsdXN0ZXIgPT0gNV0gPSAiIzNDNTQ4OEZGIgpjbHVzdGVyW2NsdXN0ZXIgPT0gNl0gPSAiI0YzOUI3RkZGIgpjbHVzdGVyW2NsdXN0ZXIgPT0gN10gPSAiI0IwOUM4NUZGIgoKCnBsb3QubmV3KCkKcGxvdChkZW5kLGhvcml6PVQsIGF4ZXM9VCx4bGltID0gYygxMDAsMCksbGVhZmxhYiA9ICJub25lIikKYWJsaW5lKHYgPSA0NywgbHR5ID0gMikKY29sb3JlZF9iYXJzKGNsdXN0ZXIsZGVuZCxob3JpeiA9IFQsc29ydF9ieV9sYWJlbHNfb3JkZXIgPSBGLHlfc2hpZnQgPSAxLAogICAgICAgICAgICAgICByb3dMYWJlbHM9ICIiICkKZ3JpZEdyYXBoaWNzOjpncmlkLmVjaG8oKQp0cmVlIDwtIGdyaWQuZ3JhYigpCmBgYApgYGB7ciBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9N30KcGFyX3BjYSA9IHBjYV8xW2NvbG5hbWVzKGcuc3BhY2UpW2NvbG5hbWVzKGcuc3BhY2UpICVpbiUgcm93bmFtZXMocGNhXzEpXSxdCiNwbG90IE4gMQpwMSA8LSBnZ3BhcmFncmFwaCh0ZXh0ID0gcGFzdGUwKHJvd25hbWVzKHBhcl9wY2FbcGFyX3BjYSRoY2x1c3QgPT0gcGFyX3BjYVsiRmV6ZjIiLCJoY2x1c3QiXSxdKSwgY29sbGFwc2UgPSAiLCAiKSwgCiAgICAgICAgICAgICAgICAgIGZhY2UgPSAiaXRhbGljIiwgCiAgICAgICAgICAgICAgICAgIHNpemUgPTEwLCAKICAgICAgICAgICAgICAgICAgY29sb3IgPSAiIzNDNTQ4OEZGIikKCiNwbG90IE4gMgpwMiA9IGdncGFyYWdyYXBoKHRleHQgPSBwYXN0ZTAocm93bmFtZXMocGFyX3BjYVtwYXJfcGNhJGhjbHVzdCA9PSB1bmlxdWUocGFyX3BjYSRoY2x1c3QpWyF1bmlxdWUocGFyX3BjYSRoY2x1c3QpICVpbiUgdW5pcXVlKHBhcl9wY2FbdW5saXN0KGxheWVycyksImhjbHVzdCJdKV1bMV0sXSksIGNvbGxhcHNlID0gIiwgIiksIAogICAgICAgICAgICAgICAgIGZhY2UgPSAiaXRhbGljIiwgCiAgICAgICAgICAgICAgICAgc2l6ZSA9MTAsIAogICAgICAgICAgICAgICAgIGNvbG9yID0gImdyYXkiKQoKI3Bsb3QgTiAzCnAzID0gZ2dwYXJhZ3JhcGgodGV4dCA9IHBhc3RlMChyb3duYW1lcyhwYXJfcGNhW3Bhcl9wY2EkaGNsdXN0ID09IHVuaXF1ZShwYXJfcGNhJGhjbHVzdClbIXVuaXF1ZShwYXJfcGNhJGhjbHVzdCkgJWluJSB1bmlxdWUocGFyX3BjYVt1bmxpc3QobGF5ZXJzKSwiaGNsdXN0Il0pXVsyXSxdKSwgY29sbGFwc2UgPSAiLCAiKSwgCiAgICAgICAgICAgICAgICAgZmFjZSA9ICJpdGFsaWMiLCAKICAgICAgICAgICAgICAgICBzaXplID0xMCwgCiAgICAgICAgICAgICAgICAgY29sb3IgPSAiZ3JheSIpCiNwbG90IE4gNApwNCA9IGdncGFyYWdyYXBoKHRleHQgPSBwYXN0ZTAocm93bmFtZXMocGFyX3BjYVtwYXJfcGNhJGhjbHVzdCA9PSBwYXJfcGNhWyJSZWxuIiwiaGNsdXN0Il0sXSksIGNvbGxhcHNlID0gIiwgIiksIAogICAgICAgICAgICAgICAgIGZhY2UgPSAiaXRhbGljIiwgCiAgICAgICAgICAgICAgICAgc2l6ZSA9MTAsIAogICAgICAgICAgICAgICAgIGNvbG9yID0gIiNFNjRCMzVGRiIpCgojcGxvdCBOIDUKcDUgPSBnZ3BhcmFncmFwaCh0ZXh0ID0gcGFzdGUwKHJvd25hbWVzKHBhcl9wY2FbcGFyX3BjYSRoY2x1c3QgPT0gcGFyX3BjYVsiQ3V4MSIsImhjbHVzdCJdLF0pLCBjb2xsYXBzZSA9ICIsICIpLCAKICAgICAgICAgICAgICAgICBmYWNlID0gIml0YWxpYyIsIAogICAgICAgICAgICAgICAgIHNpemUgPTEwLCAKICAgICAgICAgICAgICAgICBjb2xvciA9IHBhcl9wY2FbIkN1eDEiLCJjb2xvcnMiXSkKI3Bsb3QgTiA2CnA2ID0gZ2dwYXJhZ3JhcGgodGV4dCA9IHBhc3RlMChyb3duYW1lcyhwYXJfcGNhW3Bhcl9wY2EkaGNsdXN0ID09IHBhcl9wY2FbIlJvcmIiLCJoY2x1c3QiXSxdKSwgY29sbGFwc2UgPSAiLCAiKSwgCiAgICAgICAgICAgICAgICAgZmFjZSA9ICJpdGFsaWMiLCAKICAgICAgICAgICAgICAgICBzaXplID0xMCwgCiAgICAgICAgICAgICAgICAgY29sb3IgPSBwYXJfcGNhWyJSb3JiIiwiY29sb3JzIl0pCiNwbG90IE4gNwpwNyA9IGdncGFyYWdyYXBoKHRleHQgPSBwYXN0ZTAocm93bmFtZXMocGFyX3BjYVtwYXJfcGNhJGhjbHVzdCA9PSBwYXJfcGNhWyJWaW0iLCJoY2x1c3QiXSxdKSwgY29sbGFwc2UgPSAiLCAiKSwgCiAgICAgICAgICAgICAgICAgZmFjZSA9ICJpdGFsaWMiLCAKICAgICAgICAgICAgICAgICBzaXplID0xMCwgCiAgICAgICAgICAgICAgICAgY29sb3IgPSBwYXJfcGNhWyJWaW0iLCJjb2xvcnMiXSkKCncgPSBnZ3BhcmFncmFwaCh0ZXh0ID0gIiAiLCAKICAgICAgICAgICAgICAgICBmYWNlID0gIml0YWxpYyIsIAogICAgICAgICAgICAgICAgIHNpemUgPTEwLCAKICAgICAgICAgICAgICAgICBjb2xvciA9ICJ3aGl0ZSIpCgpwcCA9Z2dhcnJhbmdlKHAzLHA1LHAxLHAyLHA0LHA2LHA3LHcsCiAgICAgICAgICBuY29sID0gMSwgbnJvdyA9IDgsCiAgICAgICAgICBoZWlnaHRzID0gYygwLjEsMC4xNSwwLjIzLCAwLjEsIDAuMiwgMC4yLCAwLjEsIDAuMzUpKQoKCiAgICAKbGF5IDwtIHJiaW5kKGMoMSxOQSksCiAgICAgICAgICAgICBjKDEsMi41KSwKICAgICAgICAgICAgIGMoMSwyLjUpLAogICAgICAgICAgICAgYygxLDIuNSksCiAgICAgICAgICAgICBjKDEsMi41KSwKICAgICAgICAgICAgIGMoMSwyLjUpLAogICAgICAgICAgICAgYygxLE5BKSkKCmdyaWRFeHRyYTo6Z3JpZC5hcnJhbmdlKHRyZWUsIHBwLCBsYXlvdXRfbWF0cml4ID0gbGF5KQoKYGBgCgpvciBqdXN0IHdpdGggcHJpbWFyeSBtYXJrZXJzCgpgYGB7ciBmaWcud2lkdGg9IDEwfQojIHNvbWUgbW9yZSBnZW5lcyBhcyBsYW5kbWFya3MKY29udHJvbHMgPWxpc3QoImdlbmVzIHJlbGF0ZWQgdG8gTDUvNiI9YygiRm94cDIiLCJUYnIxIiksICJnZW5lcyByZWxhdGVkIHRvIEwyLzMiPWMoIk1lZjJjIiksICJnZW5lcyByZWxhdGVkIHRvIFByb2ciPWMoIk5lcyIsIlNveDIiKSAsICJnZW5lcyByZWxhdGVkIHRvIEwxIj1jKCkgLCAiZ2VuZXMgcmVsYXRlZCB0byBMNCI9YygpKSAKZGVuZCAlPiUKZGVuZGV4dGVuZDo6c2V0KCJsYWJlbHMiLCBpZmVsc2UobGFiZWxzKGRlbmQpICVpbiUgcm93bmFtZXMocGNhXzEpW3Jvd25hbWVzKHBjYV8xKSAlaW4lIGModW5saXN0KGxheWVycyksdW5saXN0KGNvbnRyb2xzKSldLCBsYWJlbHMoZGVuZCksICIiKSkgJT4lCiAgZGVuZGV4dGVuZDo6c2V0KCJicmFuY2hlc19rX2NvbG9yIiwgdmFsdWUgPSBjKCJncmF5ODAiLCIjNERCQkQ1RkYiLCIjOTFEMUMyRkYiICwiZ3JheTgwIiwiI0YzOUI3RkZGIiwiI0U2NEIzNUZGIiwiIzNDNTQ4OEZGIiksIGsgPSA3KSAlPiUKcGxvdChob3Jpej1GLCBheGVzPVQseWxpbSA9IGMoMCwxMDApKQoKYGBgCgoKCgpOb3cgd2UgY2FuIHBsb3QgdGhlIFBDQQoKYGBge3J9CiMgZGF0YWZyYW1lIHRvIGJlIGFibGUgdG8gbGFiZWwgb25seSBwcmltYXJ5IG1hcmtlcnMgYW5kIGNvbnRyb2wgZ2VuZXMKdGV4dGRmIDwtIHBjYV8xW3Jvd25hbWVzKHBjYV8xKSAlaW4lIGModW5saXN0KGxheWVycyksdW5saXN0KGNvbnRyb2xzKSkgLCBdCgogZm9yIChtIGluIGMoMTpsZW5ndGgoY29udHJvbHMpKSkgewogIGZvciAoZyBpbiBjb250cm9sc1tbbV1dKSB7CiAgICBpZihnICVpbiUgcm93bmFtZXModGV4dGRmKSl7CiAgICAgIHRleHRkZltnLCJoaWdobGlnaHQiXSA9IG5hbWVzKGNvbnRyb2xzW21dKQogICAgfSAKICB9Cn0KCiMgZGVjaWRpbmcgdGhlIGNvbG9ycwpteWNvbG91cnMgPC0gYygiZ2VuZXMgcmVsYXRlZCB0byBMNS82IiA9ICIjM0M1NDg4RkYiLCJnZW5lcyByZWxhdGVkIHRvIEwyLzMiPSIjRjM5QjdGRkYiLCJnZW5lcyByZWxhdGVkIHRvIFByb2ciPSIjNERCQkQ1RkYiLCJnZW5lcyByZWxhdGVkIHRvIEwxIj0iI0U2NEIzNUZGIiwiZ2VuZXMgcmVsYXRlZCB0byBMNCIgPSAiIzkxRDFDMkZGIiwgIm5vdCBtYXJrZWQiPSIjQjA5Qzg1RkYiKQoKIyB0byBhc3NpbmcgY29ycmVjbHkgdGhlIGNsdXN0ZXIgbnVtYmVyIGFuZCB0aGUgY29sb3IKbXljb2xvdXJzMiA9IGMoIlJlbG4iLCJTYXRiMiIsIlJvcmIiLCJCY2wxMWIiLCJWaW0iKQpuYW1lcyhteWNvbG91cnMyKSA9IHVuaXF1ZShjdXRbdW5saXN0KGxheWVycyldKQoKbXljb2xvdXJzMltteWNvbG91cnMyID09ICJSZWxuIl0gPSAiI0U2NEIzNUZGIgpteWNvbG91cnMyW215Y29sb3VyczIgPT0gIlNhdGIyIl0gPSAiI0YzOUI3RkZGIgpteWNvbG91cnMyW215Y29sb3VyczIgPT0gIlJvcmIiXSA9ICIjOTFEMUMyRkYiCm15Y29sb3VyczJbbXljb2xvdXJzMiA9PSAiQmNsMTFiIl0gPSAiIzNDNTQ4OEZGIgpteWNvbG91cnMyW215Y29sb3VyczIgPT0gIlZpbSJdID0gIiM0REJCRDVGRiIKY29sb3JfdG9fYWRkID0gdW5pcXVlKHBjYV8xJGhjbHVzdClbIXVuaXF1ZShwY2FfMSRoY2x1c3QpICVpbiUgYXMubnVtZXJpYyhuYW1lcyhteWNvbG91cnMyKSldCm5hbWVzKGNvbG9yX3RvX2FkZCkgPSB1bmlxdWUocGNhXzEkaGNsdXN0KVshdW5pcXVlKHBjYV8xJGhjbHVzdCkgJWluJSBhcy5udW1lcmljKG5hbWVzKG15Y29sb3VyczIpKV0KY29sb3JfdG9fYWRkW2NvbG9yX3RvX2FkZCAlaW4lIAogICAgICAgICAgICAgICAgIHVuaXF1ZShwY2FfMSRoY2x1c3QpWyF1bmlxdWUocGNhXzEkaGNsdXN0KSAlaW4lIGFzLm51bWVyaWMobmFtZXMobXljb2xvdXJzMikpXV0gPSAiI0IwOUM4NUZGIgpteWNvbG91cnMyID0gYyhteWNvbG91cnMyLGNvbG9yX3RvX2FkZCkKCnBjYTEgPSBnZ3Bsb3Qoc3Vic2V0KHBjYV8xLCFoY2x1c3QgJWluJSB1bmlxdWUoY3V0W3VubGlzdChsYXllcnMpXSkgICksIGFlcyh4PVBDMSwgeT1QQzIpKSArICBnZW9tX3BvaW50KGFscGhhID0gMC4zLGNvbG9yID0gIiNCMDlDODVGRiIsc2l6ZT0xKQoKcGNhXzEkbmFtZXMgPSByb3duYW1lcyhwY2FfMSkKI3BjYTIgPSBwY2ExICsgZ2VvbV9wb2ludChkYXRhID0gc3Vic2V0KHBjYV8xLCBoaWdobGlnaHQgIT0gIm5vdCBtYXJrZWQiICksIGFlcyh4PVBDMSwgeT1QQzIsIGNvbG91cj1oY2x1c3QpLHNpemU9Mi41LGFscGhhID0gMC45KSAKcGNhMiA9IHBjYTEgKyBnZW9tX3BvaW50KGRhdGEgPSBzdWJzZXQocGNhXzEsIGhjbHVzdCAlaW4lIHVuaXF1ZShjdXRbdW5saXN0KGxheWVycyldKSApLCBhZXMoeD1QQzEsIHk9UEMyLCBjb2xvdXI9YXMuY2hhcmFjdGVyKGhjbHVzdCkpLHNpemU9MSxhbHBoYSA9IDAuNSkgKyAKIHNjYWxlX2NvbG9yX21hbnVhbCggIlN0YXR1cyIsIHZhbHVlcyA9IG15Y29sb3VyczIpICArCiAgc2NhbGVfZmlsbF9tYW51YWwoICJTdGF0dXMiLCB2YWx1ZXMgPSBteWNvbG91cnMyKSAgKwogIHhsYWIoIiIpICsgeWxhYigiIikgKwogIGdlb21fbGFiZWxfcmVwZWwoZGF0YSA9dGV4dGRmICwgYWVzKHggPSBQQzEsIHkgPSBQQzIsIGxhYmVsID0gcm93bmFtZXModGV4dGRmKSxmaWxsPWFzLmNoYXJhY3RlcihoY2x1c3QpKSwKICAgICAgICAgICAgICAgICAgIGxhYmVsLnNpemUgPSBOQSwgCiAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDAuNSwgCiAgICAgICAgICAgICAgICAgICBkaXJlY3Rpb24gPSAiYm90aCIsCiAgICAgICAgICAgICAgICAgICBuYS5ybT1UUlVFLAogICAgICAgICAgICAgICAgICAgc2VlZCA9IDEyMzQpICsKICBnZW9tX2xhYmVsX3JlcGVsKGRhdGEgPXRleHRkZiAsIGFlcyh4ID0gUEMxLCB5ID0gUEMyLCBsYWJlbCA9IHJvd25hbWVzKHRleHRkZikpLAogICAgICAgICAgICAgICAgICAgbGFiZWwuc2l6ZSA9IE5BLCAKICAgICAgICAgICAgICAgICAgIHNlZ21lbnQuY29sb3IgPSAnYmxhY2snLAogICAgICAgICAgICAgICAgICAgc2VnbWVudC5zaXplID0gMC41LAogICAgICAgICAgICAgICAgICAgZGlyZWN0aW9uID0gImJvdGgiLAogICAgICAgICAgICAgICAgICAgYWxwaGEgPSAxLCAKICAgICAgICAgICAgICAgICAgIG5hLnJtPVRSVUUsCiAgICAgICAgICAgICAgICAgICBmaWxsID0gTkEsCiAgICAgICAgICAgICAgICAgICBzZWVkID0gMTIzNCkgKwogIGdndGl0bGUoIlBDQSIpICsKICB0aGVtZV9saWdodChiYXNlX3NpemU9MTApICsKICAgIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfYmxhbmsoKSxwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWNlPSJpdGFsaWMiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3I9IiMzQzU0ODhGRiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhqdXN0PTAuMDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmVoZWlnaHQ9MS4yLG1hcmdpbiA9IG1hcmdpbih0ID0gNSwgYiA9IC0xNSkpLAogICAgICAgIGF4aXMudGV4dC55PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICAjIHRpdGwpCgpwY2EyICMrIGdlb21fZW5jaXJjbGUoZGF0YSA9IHBjYV8xLCBhZXMoZ3JvdXA9aGNsdXN0KSkgCmBgYAoKCmBgYHtyIH0Kc2V0LnNlZWQoTlVMTCkKY2VsbCA9IHNhbXBsZShuY29sKG9iakUxN0ByYXcpLDEscmVwbGFjZSA9IFQpCnByaW50KGNlbGwpCmdlbmVzLnRvLmNvbG9yLnJlZCA9IHdoaWNoKG9iakUxN0ByYXdbLGNlbGxdID4gMCkKbGVuZ3RoKGdlbmVzLnRvLmNvbG9yLnJlZCkKCnBjYTMgPSBwY2EyICsgZ2VvbV9wb2ludChkYXRhID0gc3Vic2V0KHBjYV8xLCBuYW1lcyAlaW4lIG5hbWVzKGdlbmVzLnRvLmNvbG9yLnJlZCkpLCBjb2xvcj0icmVkIikKCnBjYTMKYGBgCgoKCnQtU05FIGNvZGUgYW5kIHBsb3QKCmBgYHtyfQojIHJ1biB0aGUgdC1TTkUKY2wuZ2VuZXMudHNuZSA9IFJ0c25lKGcuc3BhY2UgLGluaXRpYWxfZGltcyA9IDEwMCwgZGltcyA9IDIsIHBlcnBsZXhpdHk9MzAsZXRhID0gMjAwLCB2ZXJib3NlPUYsIG1heF9pdGVyID0gMzAwMCx0aGV0YT0wLjQsbnVtX3RocmVhZHMgPSAxMCxwY2FfY2VudGVyID0gVCwgcGNhX3NjYWxlID0gRkFMU0UsIG5vcm1hbGl6ZSA9IFQgKQoKZF90c25lXzEgPSBhcy5kYXRhLmZyYW1lKGNsLmdlbmVzLnRzbmUkWSkKcm93bmFtZXMoZF90c25lXzEpID0gcm93bmFtZXMoZy5zcGFjZSkKCmRfdHNuZV8xID0gZF90c25lXzFbb3JkZXIuZGVuZHJvZ3JhbShkZW5kKSxdCgojIHNhdmUgdGhlIGNsdXN0ZXIgbnVtZWJyIGluc2lkZSBhIGRhdGFmcmFtZSB3aXRoIHRoZSB0LVNORSBpbmZvcm1hdGlvbgpkX3RzbmVfMSRoY2x1c3QgPSBjdXQKCmRfdHNuZV8xJG5hbWVzID0gcm93bmFtZXMoZF90c25lXzEpCgojIGFzIGJlZm9yZSB0byBsYWJlbCBvbmx5IHNvbWUgZ2VuZXMKdGV4dGRmIDwtIGRfdHNuZV8xW3Jvd25hbWVzKGRfdHNuZV8xKSAlaW4lIGModW5saXN0KGxheWVycyksdW5saXN0KGNvbnRyb2xzKSksXQoKZm9yIChtIGluIGMoMTpsZW5ndGgoY29udHJvbHMpKSkgewogIGZvciAoZyBpbiBjb250cm9sc1tbbV1dKSB7CiAgICBpZihnICVpbiUgcm93bmFtZXModGV4dGRmKSl7CiAgICAgIHRleHRkZltnLCJoaWdobGlnaHQiXSA9IG5hbWVzKGNvbnRyb2xzW21dKQogICAgfSAKICB9Cn0KCgogcDEgPSBnZ3Bsb3Qoc3Vic2V0KGRfdHNuZV8xLCFoY2x1c3QgJWluJSB1bmlxdWUoY3V0W3VubGlzdChsYXllcnMpXSkpLCBhZXMoeD1WMSwgeT1WMikpICsgIGdlb21fcG9pbnQoYWxwaGEgPSAwLjMsIGNvbG9yID0gIiNCMDlDODVGRiIsIHNpemU9MSkKCnAyID0gcDEgKyBnZW9tX3BvaW50KGRhdGEgPSBzdWJzZXQoZF90c25lXzEsIGhjbHVzdCAlaW4lIHVuaXF1ZShjdXRbdW5saXN0KGxheWVycyldKSApLCBhZXMoeD1WMSwgeT1WMiwgY29sb3VyPWFzLmNoYXJhY3RlcihoY2x1c3QpKSxzaXplPTEsYWxwaGEgPSAwLjUpICsKICAgIHNjYWxlX2NvbG9yX21hbnVhbCgiU3RhdHVzIiwgdmFsdWVzID0gbXljb2xvdXJzMikgICsKICBzY2FsZV9maWxsX21hbnVhbCgiU3RhdHVzIiwgdmFsdWVzID0gbXljb2xvdXJzMikgICsKICB4bGFiKCIiKSArIHlsYWIoIiIpKwogIGdlb21fbGFiZWxfcmVwZWwoZGF0YSA9dGV4dGRmICwgYWVzKHggPSBWMSwgeSA9IFYyLCBsYWJlbCA9IG5hbWVzLGZpbGw9YXMuY2hhcmFjdGVyKGhjbHVzdCkpLAogICAgICAgICAgICAgICAgICAgbGFiZWwuc2l6ZSA9IE5BLCAKICAgICAgICAgICAgICAgICAgIGFscGhhID0gMC41LCAKICAgICAgICAgICAgICAgICAgIGRpcmVjdGlvbiA9ICJib3RoIiwKICAgICAgICAgICAgICAgICAgIG5hLnJtPVRSVUUsCiAgICAgICAgICAgICAgICAgICBzZWVkID0gMTIzNCkgKwogIGdlb21fbGFiZWxfcmVwZWwoZGF0YSA9dGV4dGRmICwgYWVzKHggPSBWMSwgeSA9IFYyLCBsYWJlbCA9IG5hbWVzKSwKICAgICAgICAgICAgICAgICAgIGxhYmVsLnNpemUgPSBOQSwgCiAgICAgICAgICAgICAgICAgICBzZWdtZW50LmNvbG9yID0gJ2JsYWNrJywKICAgICAgICAgICAgICAgICAgIHNlZ21lbnQuc2l6ZSA9IDAuNSwKICAgICAgICAgICAgICAgICAgIGRpcmVjdGlvbiA9ICJib3RoIiwKICAgICAgICAgICAgICAgICAgIGFscGhhID0gMSwgCiAgICAgICAgICAgICAgICAgICBuYS5ybT1UUlVFLAogICAgICAgICAgICAgICAgICAgZmlsbCA9IE5BLAogICAgICAgICAgICAgICAgICAgc2VlZCA9IDEyMzQpICsKICBnZ3RpdGxlKCJ0LVNORSIpICsKICB0aGVtZV9saWdodChiYXNlX3NpemU9MTApICsKICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X2JsYW5rKCkscGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTE0LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFjZT0iaXRhbGljIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yPSIjM0M1NDg4RkYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoanVzdD0wLjAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaW5laGVpZ2h0PTEuMixtYXJnaW4gPSBtYXJnaW4odCA9IDUsIGIgPSAtMTUpKSwKICAgICAgICBheGlzLnRleHQueT1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSAgIyB0aXRsKQpwMgpgYGAKQ29kZSB0byBjcmVhdGUgYW4gaXRlcmFjdGl2ZSBwbG90LiBUaGlzIGNhbiBiZSBtb2RpZmllZCB0byBiZSB1c2VkIHdpdGggYWxsIHRoZSBwbG90cy4KCmBgYHtyIGVjaG89VFJVRX0KCnAgPSBnZ3Bsb3QoZF90c25lXzEsIGFlcyh4PVYxLCB5PVYyLCB0ZXh0PSBwYXN0ZSgiZ2VuZTogIixuYW1lcykpKSArICAKICBnZW9tX3BvaW50KHNpemU9MiwgYWVzKGNvbG91cj1hcy5jaGFyYWN0ZXIoaGNsdXN0KSksIGFscGhhPTAuOCkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCgiU3RhdHVzIiwgdmFsdWVzID0gbXljb2xvdXJzMikgICsKICB4bGFiKCIiKSArIHlsYWIoIiIpICsKICBnZ3RpdGxlKCJ0LVNORSIpICsKICB0aGVtZV9saWdodChiYXNlX3NpemU9MTApICsKICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF9ibGFuaygpKQoKZ2dwbG90bHkocCkKYGBgCgpNdWx0aWRpbWVuc2lvbmFsIHNjYWxpbmcgKE1EUykgYW5kIHBsb3QKYGBge3J9CiMgcnVuIHRoZSBNRFMKZ2VuZXMuZGlzdC5ldWMgPSAgZGlzdChnLnNwYWNlLCBtZXRob2QgPSAgImV1Y2xpZGVhbiIpCiNmaXQgPC0gaXNvTURTKGdlbmVzLmRpc3QuZXVjKSAjIG5vdCBsaW5lYXIKZml0IDwtIGlzb01EUyhnZW5lcy5kaXN0LmV1YykKCmZpdC5nZW5lcyA9IGFzLmRhdGEuZnJhbWUoZml0JHBvaW50cykKCmZpdC5nZW5lcyA9IGZpdC5nZW5lc1tvcmRlci5kZW5kcm9ncmFtKGRlbmQpLF0KCmZpdC5nZW5lcyRoY2x1c3QgPSBjdXQKCgpmaXQuZ2VuZXMkbmFtZXMgPSByb3duYW1lcyhmaXQuZ2VuZXMpCgpteWNvbG91cnMzIDwtIGMoImNsdXN0ZXIgTDUvNiBtYXJrZXJzIiA9ICIjM0M1NDg4RkYiLCJjbHVzdGVyIEwyLzMgbWFya2VycyI9IiNGMzlCN0ZGRiIsImNsdXN0ZXIgUHJvZyBtYXJrZXJzIj0iIzREQkJENUZGIiwiY2x1c3RlciBMMSBtYXJrZXJzIj0iI0U2NEIzNUZGIiwiY2x1c3RlciBMNCBtYXJrZXJzIiA9ICIjOTFEMUMyRkYiLCAibm90IGlkZW50aWZpZWQgY2x1c3RlciI9IiNCMDlDODVGRiIpCgojbXljb2xvdXJzMyA8LSBjKCJjbHVzdGVyIGxheWVyIFYtVkkgbWFya2VycyIgPSAiIzNDNTQ4OEZGIiwiY2x1c3RlciBsYXllciBJSS1JSUkgbWFya2VycyI9IiNGMzlCN0ZGRiIsImNsdXN0ZXIgcHJvZ2VuaXRvciBtYXJrZXJzIj0iIzREQkJENUZGIiwiY2x1c3RlciBsYXllciBJIG1hcmtlcnMiPSIjRTY0QjM1RkYiLCJjbHVzdGVyIGxheWVyIElWIG1hcmtlcnMiID0gIiM5MUQxQzJGRiIsICJub3QgaWRlbnRpZmllZCBjbHVzdGVyIj0iI0IwOUM4NUZGIikKCgojZml0LmdlbmVzJGhjbHVzdCA9IGZhY3RvcihjdXRyZWUoaGMubm9ybSwgNykpCnVzZWQgPSB2ZWN0b3IoKQpmb3IgKGsgaW4gYygxOmxlbmd0aChsYXllcnMpKSkgewogICNwcmludChrKQogIHR0ID1hcy5udW1lcmljKGN1dFtsYXllcnNbW2tdXV1bMV0pCiAgZml0LmdlbmVzW2ZpdC5nZW5lcyRoY2x1c3QgPT0gdHQsImNsdXN0ZXIiXSA9IHBhc3RlKCJjbHVzdGVyIixuYW1lcyhsYXllcnNba10pLCJtYXJrZXJzIiwgc2VwID0gIiAiICkKICB1c2VkID0gYyh1c2VkLGN1dFtsYXllcnNbW2tdXV1bMV0pCn0KCmZpdC5nZW5lc1tmaXQuZ2VuZXMkaGNsdXN0ICVpbiUgKHVuaXF1ZShmaXQuZ2VuZXMkaGNsdXN0KVshdW5pcXVlKGZpdC5nZW5lcyRoY2x1c3QpICVpbiUgdXNlZF0pLF0kY2x1c3RlciA9ICJub3QgaWRlbnRpZmllZCBjbHVzdGVyIgoKdGV4dGRmIDwtIGZpdC5nZW5lc1tyb3duYW1lcyhmaXQuZ2VuZXMpICVpbiUgYyh1bmxpc3QobGF5ZXJzKSx1bmxpc3QoY29udHJvbHMpKSxdCgogICBmMSA9IGdncGxvdChzdWJzZXQoZml0LmdlbmVzLCFoY2x1c3QgJWluJSB1bmlxdWUoY3V0W3VubGlzdChsYXllcnMpXSkgKSwgYWVzKHg9VjEsIHk9VjIpKSArICBnZW9tX3BvaW50KGFscGhhID0gMC4zLCBjb2xvciA9ICIjQjA5Qzg1RkYiLCBzaXplPTEpCgpmMiA9IGYxICsgZ2VvbV9wb2ludChkYXRhID0gc3Vic2V0KGZpdC5nZW5lcywgaGNsdXN0ICVpbiUgdW5pcXVlKGN1dFt1bmxpc3QobGF5ZXJzKV0pICksIAogICAgICAgICAgICAgICAgICAgICBhZXMoeD1WMSwgeT1WMiwgY29sb3VyPWNsdXN0ZXIpLCBzaXplPTEsYWxwaGEgPSAwLjUpICsKICBzY2FsZV9jb2xvcl9tYW51YWwoIlN0YXR1cyIsIHZhbHVlcyA9IG15Y29sb3VyczMsCiAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIkxheWVyIEkgY2x1c3RlciAiLCJMYXllcnMgSUkvSUlJIGNsdXN0ZXIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJMYXllciBJViBjbHVzdGVyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTGF5ZXJzIFYvVkkgY2x1c3RlciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlByb2dlbml0b3JzIGNsdXN0ZXIiKSApICArCiAgc2NhbGVfZmlsbF9tYW51YWwoIlN0YXR1cyIsIHZhbHVlcyA9IG15Y29sb3VyczMpICArIAogIHhsYWIoIiIpICsgeWxhYigiIikrCiAgZ2VvbV9sYWJlbF9yZXBlbChkYXRhID10ZXh0ZGYgLCBhZXMoeCA9IFYxLCB5ID0gVjIsIGxhYmVsID0gcm93bmFtZXModGV4dGRmKSxmaWxsPWNsdXN0ZXIpLAogICAgICAgICAgICAgICAgICAgbGFiZWwuc2l6ZSA9IE5BLCAKICAgICAgICAgICAgICAgICAgIGFscGhhID0gMC41LCAKICAgICAgICAgICAgICAgICAgIGRpcmVjdGlvbiA9ImJvdGgiLAogICAgICAgICAgICAgICAgICAgbmEucm09VFJVRSwKICAgICAgICAgICAgICAgICAgIHNlZWQgPSAxMjM0LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgZ2VvbV9sYWJlbF9yZXBlbChkYXRhID10ZXh0ZGYgLCBhZXMoeCA9IFYxLCB5ID0gVjIsIGxhYmVsID0gcm93bmFtZXModGV4dGRmKSksCiAgICAgICAgICAgICAgICAgICBsYWJlbC5zaXplID0gTkEsIAogICAgICAgICAgICAgICAgICAgc2VnbWVudC5jb2xvciA9ICdibGFjaycsCiAgICAgICAgICAgICAgICAgICBzZWdtZW50LnNpemUgPSAwLjUsCiAgICAgICAgICAgICAgICAgICBkaXJlY3Rpb24gPSAiYm90aCIsCiAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDEsIAogICAgICAgICAgICAgICAgICAgbmEucm09VFJVRSwKICAgICAgICAgICAgICAgICAgIGZpbGwgPSBOQSwKICAgICAgICAgICAgICAgICAgIHNlZWQgPSAxMjM0LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgZ2d0aXRsZSgiTURTIikgKwogIHRoZW1lX2xpZ2h0KGJhc2Vfc2l6ZT0xMCkgKwogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfYmxhbmsoKSxwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWNlPSJpdGFsaWMiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3I9IiMzQzU0ODhGRiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhqdXN0PTAuMDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmVoZWlnaHQ9MS4yLG1hcmdpbiA9IG1hcmdpbih0ID0gNSwgYiA9IC0xNSkpLAogICAgICAgIGF4aXMudGV4dC55PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiIzNDNTQ4OEZGIixmYWNlID0iaXRhbGljIiApLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSAgIyB0aXRsKQoKCmYyICsgc2NhbGVfeV9yZXZlcnNlKCkrIHNjYWxlX3hfcmV2ZXJzZSgpIysgZ2VvbV9lbmNpcmNsZShkYXRhID0gZml0LmdlbmVzLCBhZXMoZ3JvdXA9YDVfY2x1c3RlcnNgKSkgCmBgYApgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQpsaWJyYXJ5KCJBbm5vdGF0aW9uRGJpIikKbGlicmFyeSgib3JnLk1tLmVnLmRiIikKbGlicmFyeSgiR08uZGIiKQoKYGBgCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CkdPID0gIkdPOjAwMDM2NzYiICNudWNsZWljIGFjaWQgYmluZGluZwpsaWJyYXJ5KG9yZy5NbS5lZy5kYikKbGlzdCA9IHNlbGVjdChvcmcuTW0uZWcuZGIsIGtleXMgPSByb3duYW1lcyhwY2FfMSksCiAgICAgICAgICAgICAgY29sdW1ucz1jKCJTWU1CT0wiLCJHT0FMTCIpLGtleXR5cGU9IlNZTUJPTCIpICNbZml0LmdlbmVzJGhjbHVzdCAlaW4lIGMoNyw0LDEsMiwzKSxdKSwKbGlzdCA9IGxpc3RbbGlzdCRHT0FMTCA9PSBHTyxdCmxpc3QgPSBsaXN0W2NvbXBsZXRlLmNhc2VzKGxpc3QpLF0Kbm90LnVzZWZ1bCA9IGMoIk5BUyIsIlRBUyIsIklFQSIsIklDIiwiTkQiKQpsaXN0MSA9IGxpc3RbIWxpc3QkRVZJREVOQ0VBTEwgJWluJSBub3QudXNlZnVsLF0KbGlzdDEgPSB1bmlxdWUobGlzdDEkU1lNQk9MKQpwY2FfMSRHTyA9IE5BCnBjYV8xW2xpc3QxLF0kR08gPSAibnVjbGVpYyBhY2lkIGJpbmRpbmciCnBjYV8xW2xpc3QxLGMoMTE6bmNvbChwY2FfMSkpXQpgYGAKYGBge3J9CnNlc3Npb25JbmZvKCkKYGBgCgo=